//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------
#include <m8c.h>        // part specific constants and macros
#include "PSoCAPI.h"    // PSoC API definitions for all User Modules
#include "DaisyLink.h"
#include <string.h>

// NB Manufacturer Code 1 should NOT be used except by Microsoft Research modules 
// Please contact gadgeteer@microsoft.com to get a manufacturer code assigned
#define DL_MSR_MANUFACTURER_CODE  0x01

// This code specifies the type of the module (and it is per-manufacturer)
#define DL_MULTICOLORLED_TYPE 0x10

// This code specifies the version of firmware in this module (per manufacturer and module type)
#define DL_MULTICOLORLED_VERSION 0x01

#define MODE_OFF                  0
#define MODE_CONSTANT             1
#define MODE_BLINK_ONCE           2
#define MODE_BLINK_CONTINUOUS     3
#define MODE_FADE_ONCE            4
#define MODE_FADE_CONTINUOUS      5
#define MODE_BLINK_ONCE_INT       6
#define MODE_BLINK_CONTINUOUS_INT 7
#define MODE_FADE_ONCE_INT        8
#define MODE_FADE_CONTINUOUS_INT  9

typedef struct RGB {
	BYTE R;
	BYTE G;
	BYTE B;
} RGB_T;

//==================================================================================
// The first 8 bytes are reserved for DaisyLink functionality.
struct I2C_Regs {    
	// DaisyLink regs. These are Read/Write since we can't protect
	// the first part of the RAM. But we overwrite them with internal values
	// if they're re-written, so they are effectively read-only.
	BYTE DLRegs[8];
	
	// Application-specific I2C regs	
	
	// Should not be written by mainboard (but we're safe if it's written)
	RGB_T CurrentColor;        // 8-10
	
	// Read/write by mainboard
	BYTE Mode;                          // 11
	BYTE Activate;                      // 12, LSB
	RGB_T Color1;                       // 13-15
	RGB_T Color2;                       // 16-18
	unsigned long Color1TimeInTicks;    // 19-22
	unsigned long Color2TimeInTicks;    // 23-26
} Reg;

#define setRGBOutput(color) {PWM8_RED_WritePulseWidth(255 - color.R);PWM8_GREEN_WritePulseWidth(255 - color.G);PWM8_BLUE_WritePulseWidth(255 - color.B); }

RGB_T Black;
BYTE direction;

unsigned long lastTicks, elapsed;
unsigned long colorTime;
long tempElapsed, tempColorTime;
long rStart, gStart, bStart;
long rVector, gVector, bVector;
long r, g, b;
int shift = 0;
	

void main(void)
{
	//=================================================
	// Initialize I2C registers
	//-------------------------------------------------
	memset(&Reg,0,sizeof(struct I2C_Regs));

	// Application
	Black.R = 0;
	Black.G = 0;
	Black.B = 0;

	//-------------------------------------------------	
	InitDaisyLink((void*)&Reg, DL_MSR_MANUFACTURER_CODE, DL_MULTICOLORLED_TYPE, DL_MULTICOLORLED_VERSION, sizeof(struct I2C_Regs)); 
	//=================================================
		
	// Initialize PWMs	
	PWM8_RED_DisableInt();
	PWM8_GREEN_DisableInt();
	PWM8_BLUE_DisableInt();
	
	PWM8_RED_Start();
	PWM8_GREEN_Start();
	PWM8_BLUE_Start();
	
	Reg.CurrentColor = Black;
	setRGBOutput(Reg.CurrentColor);

	while (1)
	{
		elapsed = GetTicks() - lastTicks;
		//=================================================
		// Required for DaisyLink functionality
		//-------------------------------------------------	

		if( 0 == DaisyLink((void*)&Reg) )
		{
			Reg.Mode = MODE_OFF;
			lastTicks = GetTicks();
		}
		
		if( Reg.Activate & 0x01 )
		{
			// clear the bit
			Reg.Activate &= 0xFE;
			lastTicks = GetTicks();
			direction = 0;
		}

		switch(Reg.Mode)
		{
		case MODE_OFF:
			Reg.CurrentColor = Black;
			break;
		case MODE_CONSTANT:
			Reg.CurrentColor = Reg.Color1;
			break;
		case MODE_BLINK_CONTINUOUS:
		case MODE_BLINK_ONCE:
		case MODE_BLINK_ONCE_INT:
		case MODE_BLINK_CONTINUOUS_INT:
			if(direction == 0)
			{
				Reg.CurrentColor = Reg.Color1;
				if( elapsed >= Reg.Color1TimeInTicks )
				{
					Reg.CurrentColor = Reg.Color2;
					if( Reg.Mode == MODE_BLINK_ONCE || Reg.Mode == MODE_BLINK_ONCE_INT)
					{
						if(Reg.Mode == MODE_BLINK_ONCE_INT) DaisyLinkInterrupt((void*)&Reg);
						Reg.Color1 = Reg.Color2;
						Reg.Mode = MODE_CONSTANT;
						break;
					}
					lastTicks = GetTicks();
					direction = 1;
				}
			} 
			else 
			{
				Reg.CurrentColor = Reg.Color2;
				if( elapsed >= Reg.Color2TimeInTicks )
				{
					if(Reg.Mode == MODE_BLINK_CONTINUOUS_INT) DaisyLinkInterrupt((void*)&Reg);
					lastTicks = GetTicks();
					Reg.CurrentColor = Reg.Color1;
					direction = 0;
				}
			}			
			break;
		case MODE_FADE_CONTINUOUS:
		case MODE_FADE_ONCE:
		case MODE_FADE_CONTINUOUS_INT:
		case MODE_FADE_ONCE_INT:
			if( direction == 0 )
			{
				colorTime = Reg.Color1TimeInTicks; // unsigned to signed cast
				rStart = Reg.Color1.R; // 0 to 255
				gStart = Reg.Color1.G;
				bStart = Reg.Color1.B;
				rVector = ((long)Reg.Color2.R) - rStart; // -255 to 255
				gVector = ((long)Reg.Color2.G) - gStart;
				bVector = ((long)Reg.Color2.B) - bStart;
			}
			else 
			{
				colorTime = Reg.Color2TimeInTicks; // unsigned to signed cast
				rStart = Reg.Color2.R;
				gStart = Reg.Color2.G;
				bStart = Reg.Color2.B;
				rVector = ((long)Reg.Color1.R) - rStart;
				gVector = ((long)Reg.Color1.G) - gStart;
				bVector = ((long)Reg.Color1.B) - bStart;
			}					
			if( elapsed >= colorTime )
			{
				if( Reg.Mode == MODE_FADE_ONCE || Reg.Mode == MODE_FADE_ONCE_INT )
				{
					// this always happen in direction=0 because it never gets to 1 for fade_once
					if(Reg.Mode == MODE_FADE_ONCE_INT) DaisyLinkInterrupt((void*)&Reg);
					Reg.Mode = MODE_CONSTANT;
					Reg.Color1 = Reg.Color2;
					Reg.CurrentColor = Reg.Color2;
					break;
				}
				if(direction == 1 && Reg.Mode == MODE_FADE_CONTINUOUS_INT) DaisyLinkInterrupt((void*)&Reg);
				direction = 1 - direction;
				lastTicks = GetTicks();
				Reg.CurrentColor = direction ? Reg.Color2 : Reg.Color1;
				break;
			}
		
			// For REALLY long fades ( > 7 minutes )
			if( elapsed > (1L<<22) )
			{
				// Right shift scale factors if they threaten to overflow long
				tempElapsed = (long)(elapsed >> 9);
				tempColorTime = (long)(colorTime >> 9);
			}
			else
			{
				tempElapsed = (long)elapsed;
				tempColorTime = (long)colorTime;
			}
				
		
			// we should be at Start + Vector * ticks/colorTime.
			r = rStart + ((rVector * tempElapsed) / tempColorTime);
			g = gStart + ((gVector * tempElapsed) / tempColorTime);
			b = bStart + ((bVector * tempElapsed) / tempColorTime);
			if( r < 0 )
				Reg.CurrentColor.R = 0;
			else if( r > 255 )
				Reg.CurrentColor.R = 255;
			else
				Reg.CurrentColor.R = (BYTE) r;
			if( g < 0 )
				Reg.CurrentColor.G = 0;
			else if( g > 255 )
				Reg.CurrentColor.G = 255;
			else
				Reg.CurrentColor.G = (BYTE) g;
			if( b < 0 )
				Reg.CurrentColor.B = 0;
			else if( b > 255 )
				Reg.CurrentColor.B = 255;
			else
				Reg.CurrentColor.B = (BYTE) b;
			break;
		}

		setRGBOutput(Reg.CurrentColor);
	}
}




